#include "../Headers/types.h"
#include "../Headers/utf8.h"

// General convert : decode
std::wstring TOB::UTF8Encoding::convert(std::string &in) {
  if(sizeof(wchar_t) == 2) {
   return convert2(in);
  } else {
   return convert4(in);
  }
}


// General convert : encode
std::string TOB::UTF8Encoding::convert(std::wstring &in) {
  if(sizeof(wchar_t) == 2) {
   return convert2(in);
  } else {
   return convert4(in);
  }
}


// Encode (2b)
std::string TOB::UTF8Encoding::convert2(std::wstring &in) {
  std::string out;
  for(uint32 i=0; i < in.size(); ++i) {
    // 0xxxxxxx
    if(in[i] < 0x80) {
      out += ((int8)in[i]);
    }
    // 110xxxxx 10xxxxxx
    else if(in[i] < 0x800) {
      out += ((int8)(MASK2BYTES | in[i] >> 6));
      out += ((int8)(MASKBYTE | (in[i] & MASKBITS)));
    }
    // 1110xxxx 10xxxxxx 10xxxxxx
    else if(in[i] < 0x10000) {
      out += ((int8)(MASK3BYTES | in[i] >> 12));
      out += ((int8)(MASKBYTE | ((in[i] >> 6) & MASKBITS)));
      out += ((int8)(MASKBYTE | (in[i] & MASKBITS)));
    }
  }
  return 0;
}


// Encode (4b)
std::string TOB::UTF8Encoding::convert4(std::wstring &in) {
  std::string out;
  for(uint32 i=0; i < in.size(); ++i) {
    // 0xxxxxxx
    if(in[i] < 0x80) {
      out += ((int8)in[i]);
    }
    // 110xxxxx 10xxxxxx
    else if(in[i] < 0x800) {
      out += ((int8)(MASK2BYTES | (in[i] >> 6)));
      out += ((int8)(MASKBYTE | (in[i] & MASKBITS)));
    }
    // 1110xxxx 10xxxxxx 10xxxxxx
    else if(in[i] < 0x10000) {
      out += ((int8)(MASK3BYTES | (in[i] >> 12)));
      out += ((int8)(MASKBYTE | (in[i] >> 6 & MASKBITS)));
      out += ((int8)(MASKBYTE | (in[i] & MASKBITS)));
    }
    // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
    else if(in[i] < 0x200000) {
      out += ((int8)(MASK4BYTES | (in[i] >> 18)));
      out += ((int8)(MASKBYTE | ((in[i] >> 12) & MASKBITS)));
      out += ((int8)(MASKBYTE | ((in[i] >> 6) & MASKBITS)));
      out += ((int8)(MASKBYTE | (in[i] & MASKBITS)));
    }
    // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
    else if(in[i] < 0x4000000) {
      out += ((int8)(MASK5BYTES | (in[i] >> 24)));
      out += ((int8)(MASKBYTE | ((in[i] >> 18) & MASKBITS)));
      out += ((int8)(MASKBYTE | ((in[i] >> 12) & MASKBITS)));
      out += ((int8)(MASKBYTE | ((in[i] >> 6) & MASKBITS)));
      out += ((int8)(MASKBYTE | (in[i] & MASKBITS)));
    }
    // 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
    else if((in[i] < 0x8000000)) {
      out += ((int8)(MASK6BYTES | in[i] >> 30));
      out += ((int8)(MASKBYTE | ((in[i] >> 18) & MASKBITS)));
      out += ((int8)(MASKBYTE | ((in[i] >> 12) & MASKBITS)));
      out += ((int8)(MASKBYTE | ((in[i] >> 6) & MASKBITS)));
      out += ((int8)(MASKBYTE | (in[i] & MASKBITS)));
    }
  }
  return out;
}


// Decode (2b)
std::wstring TOB::UTF8Encoding::convert2(std::string &in) {
  wchar_t ch;
  std::wstring out;
  for(uint32 i = 0; i < in.length();) {
   // 1110xxxx 10xxxxxx 10xxxxxx
   if((in[i] & MASK3BYTES) == MASK3BYTES) {
    ch = ((in[i] & 0x0F) << 12) | 
        ((in[i+1] & MASKBITS) << 6) | 
        (in[i+2] & MASKBITS);
    i += 3;
   } 
   // 110xxxxx 10xxxxxx
   else if((in[i] & MASK2BYTES) == MASK2BYTES) {
    ch = ((in[i] & 0x1F) << 6) | 
        (in[i+1] & MASKBITS);
    i += 2;
   }
   // 0xxxxxxx
   else if(in[i] < MASKBYTE) {
    ch = in[i];
    i += 1;
   }
   out += ch;
  }
  return out;
}


// Decode (4b)
std::wstring TOB::UTF8Encoding::convert4(std::string &in) {
  wchar_t ch;
  std::wstring out;
  for(uint32 i=0; i < in.size();) {
    // 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
    if((in[i] & MASK6BYTES) == MASK6BYTES) {
      ch = ((in[i] & 0x01) << 30) | ((in[i+1] & MASKBITS) << 24) |
           ((in[i+2] & MASKBITS) << 18) | ((in[i+3] & MASKBITS) << 12) |
           ((in[i+4] & MASKBITS) << 6) | (in[i+5] & MASKBITS);
      i += 6;
    }
    // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
    else if((in[i] & MASK5BYTES) == MASK5BYTES) {
      ch = ((in[i] & 0x03) << 24) | ((in[i+1] & MASKBITS) << 18) |
           ((in[i+2] & MASKBITS) << 12) | ((in[i+3] & MASKBITS) << 6) |
           (in[i+4] & MASKBITS);
      i += 5;
    }
    // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
    else if((in[i] & MASK4BYTES) == MASK4BYTES) {
      ch = ((in[i] & 0x07) << 18) | ((in[i+1] & MASKBITS) << 12) |
           ((in[i+2] & MASKBITS) << 6) | (in[i+3] & MASKBITS);
      i += 4;
    }
    // 1110xxxx 10xxxxxx 10xxxxxx
    else if((in[i] & MASK3BYTES) == MASK3BYTES) {
      ch = ((in[i] & 0x0F) << 12) | ((in[i+1] & MASKBITS) << 6) |
           (in[i+2] & MASKBITS);
      i += 3;
    }
    // 110xxxxx 10xxxxxx
    else if((in[i] & MASK2BYTES) == MASK2BYTES) {
      ch = ((in[i] & 0x1F) << 6) | (in[i+1] & MASKBITS);
      i += 2;
    }
    // 0xxxxxxx
    else if(in[i] < MASKBYTE) {
      ch = in[i];
      i += 1;
    }
    out += ch;
  }
  return out;
}
